
/**
 * This class implements a Mortal Fly monster
 */
public class MortalFly extends Monster {

    //#if IMGGROUP_12x12 || IMGGROUP_12x12xLimited
        private static final byte STEP_STATE_PATROLING = 2;
        private static final byte STEP_STATE_ATTACKING = 3;
        private static final short DISTANCE_DETECTING_OFFSET = 3;
        private static final short DISTANCE_DETECTING        = 20;
        private static final short DISTANCE_EVADING          = 60;
        private static final short DISTANCE_ATTACKING        = 35;
    //#elif IMGGROUP_18x18
        private static final byte STEP_STATE_PATROLING = 3;
        private static final byte STEP_STATE_ATTACKING = 4;
        private static final short DISTANCE_DETECTING_OFFSET = 5;
        private static final short DISTANCE_DETECTING        = 30;
        private static final short DISTANCE_EVADING          = 90;
        private static final short DISTANCE_ATTACKING        = 50;
    //#elif IMGGROUP_24x24 || IMGGROUP_24x24xLimited
        private static final byte STEP_STATE_PATROLING = 4;
        private static final byte STEP_STATE_ATTACKING = 5;
        private static final short DISTANCE_DETECTING_OFFSET = 7;
        private static final short DISTANCE_DETECTING        = 40;
        private static final short DISTANCE_EVADING          = 120;
        private static final short DISTANCE_ATTACKING        = 65;
    //#elif IMGGROUP_36x36
        private static final byte STEP_STATE_PATROLING = 6;
        private static final byte STEP_STATE_ATTACKING = 8;
        private static final short DISTANCE_DETECTING_OFFSET = 10;
        private static final short DISTANCE_DETECTING        = 60;
        private static final short DISTANCE_EVADING          = 180;
        private static final short DISTANCE_ATTACKING        = 100;
    //#endif

    private static final byte OBSTACLE_NONE         = 0;
    private static final byte OBSTACLE_WALL_TOP     = (1 << 0);
    private static final byte OBSTACLE_WALL_BOTTOM  = (1 << 1);
    private static final byte OBSTACLE_WALL_LEFT    = (1 << 2);
    private static final byte OBSTACLE_WALL_RIGHT   = (1 << 3);
   
    private static final int OBSTACLE_MARV_NONE        = 0;
    private static final int OBSTACLE_MARV_TOP         = (1 << 1);
    private static final int OBSTACLE_MARV_BOTTOM      = (1 << 2);
    private static final int OBSTACLE_MARV_LEFT        = (1 << 3);
    private static final int OBSTACLE_MARV_RIGHT       = (1 << 4);
    private static final int OBSTACLE_MARV_LEFTTOP     = (1 << 5);
    private static final int OBSTACLE_MARV_RIGHTTOP    = (1 << 6);
    private static final int OBSTACLE_MARV_LEFTBOTTOM  = (1 << 7);
    private static final int OBSTACLE_MARV_RIGHTBOTTOM = (1 << 8);

    private static final byte WAY_NONE     = 0;
    private static final byte WAY_UP       = (1 << 0);
    private static final byte WAY_DOWN     = (1 << 1);
    private static final byte WAY_LEFT     = (1 << 2);
    private static final byte WAY_RIGHT    = (1 << 3);
    private static final byte WAY_BLOCKED  = (1 << 4);

    static final byte CORRECTION_COUNT = 5;

    static final byte STATE_IDLE_WAIT_TICKS = 20;

    private byte subState;

    /**
     * This array contains a predefined path defined by checkpoints. 
     * Each element of array contains pair (x,y) and can be interpreted like <code>((x << 16) & 0xFFFF0000) | (y & 0x0000FFFF)</code>
     */
    private int[] path;
   
    /**
     * List of animation that will be used in the level
     * @see Animation
     */
    /** 
     * Current position of monster for calculation in FP
     */
    private long lPosX, lPosY;
   
    /** 
     * Current step of moving of monster for calculation in FP
     */
    private long lStep, lStepX, lStepY, lOldStepX, lOldStepY, lOffSetX, lOffSetY;
   
    /**
     *
     */
    private long[] correctionArray;
    private boolean correctionActivated, pathMustBeenCorrected;
    private byte correctionIndex;

    /**
     *
     */
    private short posLeft, posTop, posRight, posBottom, goalPosX, goalPosY, monsterWidth, monsterHeight;

    /**
     * The index shows element of array of next check pint towards which monster is moing
     */
    private byte prevCheckPointIndex, nextCheckPointIndex;
   
    /**
     * Direction of changing indexes during maving a monster from one check point to other (increasing or decreasing)
     */
    private byte nextCheckPointDir;
   
    /**
     * Corrent moving state
     */
    private byte wayCurrent, wayBlocked, wallCurrent, wallNext;

    /**
     * Counter
     */
    private byte counter;
   
    /**
     * Constructs a MortalFly class.
     * @param x the x coordinate of monster
     * @param y the y coordinate of monster
     * @param path a predefined path defined by checkpoints
     */
    public MortalFly(short x, short y, int[] path) {
        super(x, y);
      
        int i;
      
        lPosX = FP.toFP(posX);
        lPosY = FP.toFP(posY);
        lStep = FP.toFP(STEP_STATE_PATROLING);

        lStepX = lOldStepX = 0;
        lStepY = lOldStepY = 0;

        this.path = path;

        long A, B;
        correctionArray = new long[CORRECTION_COUNT + 1];
        for (i = 0; i < CORRECTION_COUNT + 1; i++) {
            B = FP.div(FP.toFP(i), FP.toFP(CORRECTION_COUNT));
            A = FP.toFP(1) - B;
            correctionArray[i] = FP.div(FP.mul(FP.toFP(2), FP.mul(FP.toFP(CORRECTION_COUNT), FP.mul(A, B))) + FP.mul(FP.toFP(CORRECTION_COUNT), FP.mul(B, B)), FP.toFP(CORRECTION_COUNT));
        }
        correctionActivated = false;
        pathMustBeenCorrected = false;
        correctionIndex = 0;

        animations = new Animation[Constants.ANIMATION_DRAGON.length];
        for (i = 0; i < animations.length; i++) {
            animations[i] = new Animation(Constants.ANIMATION_DRAGON[i]);
        }
        currentAnimation = animations[0];
      
        monsterWidth = (short)currentAnimation.GetCurFrmWidth();
        monsterHeight = (short)currentAnimation.GetCurFrmHeight();
      
        wayCurrent = wayBlocked = WAY_NONE;
        mode = MODE_MOVE_LEFT;
        nextCheckPointIndex = 0;
        nextCheckPointDir = 1;
        getNextCheckPoint();
        changeState(STATE_PATROLING);
    }

    /**
     * This method makes a "thinking" of this type of monster. Here is implemented AI.
     */
    public void update() {
        short tX, tY, tStep;
        int fMarv;
        byte fObstacles;
        if (isActive) {
            switch (currentState) {
                /*** BEGIN PATROLING STATE ***/
                case STATE_PATROLING:
                    if (isMarvWithInRange(DISTANCE_EVADING) && (Hero.m_blDynamite || Hero.m_nPickAxe > 0)) {
                        changeState(STATE_EVADING);
                    }
                    else if (isMarvWithInRange(DISTANCE_ATTACKING)) {
                        changeState(STATE_ATTACKING);
                    }
                    else {
                        tX = getX(path[nextCheckPointIndex]);
                        tY = getY(path[nextCheckPointIndex]);
                        tStep = FP.toShort(lStep);
                        if (isIntersect(posX - 1, posY - 1, posX + 1, posY + 1, tX - tStep - 1, tY - tStep - 1, tX + tStep + 1, tY + tStep + 1)) {
                            getNextCheckPoint();
                            goTo(goalPosX, goalPosY, true);
                        }
                    }
                break;
                /*** END PATROLING STATE ***/
            
                /*** BEGIN DROPING STATE  ***/
                case STATE_SEARCHING: // Looking for check point
                    if (isMarvWithInRange(DISTANCE_EVADING) && (Hero.m_blDynamite || Hero.m_nPickAxe > 0)) {
                        changeState(STATE_EVADING);
                    }
                    else if (isMarvWithInRange(DISTANCE_ATTACKING)) {
                        changeState(STATE_ATTACKING);
                    }
                    else {
                        boolean flag = false;
                        for (byte i = 0; i < path.length; i++) {
                            if (GameWorld.isPointsWithInRange(posX, posY, getX(path[i]), getY(path[i]))) {
                                flag = true;

                                prevCheckPointIndex = nextCheckPointIndex;
                                nextCheckPointIndex = i;
                                goalPosX = getX(path[nextCheckPointIndex]);
                                goalPosY = getY(path[nextCheckPointIndex]);
                                goTo(goalPosX, goalPosY, true);

                                changeState(STATE_PATROLING);
                              
                                break;
                            }
                        }
                        if (!flag) {
                            fObstacles = checkVisibleArea();
                            if (wayCurrent == WAY_NONE) {
                                if (posY > getY(path[nextCheckPointIndex]) && (fObstacles & OBSTACLE_WALL_TOP) == 0) {
                                    goTo(WAY_UP);
                                }
                                else if (posY < getY(path[nextCheckPointIndex]) && (fObstacles & OBSTACLE_WALL_BOTTOM) == 0) {
                                    goTo(WAY_DOWN);
                                }
                                else if (posX > getX(path[nextCheckPointIndex]) && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                    goTo(WAY_LEFT);
                                }
                                else if ((fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                    goTo(WAY_RIGHT);
                                }
                                else {
                                    goTo(WAY_NONE);
                                }
                            }
                            else if ((wayCurrent & WAY_LEFT) == WAY_LEFT) {
                                if (wallNext == OBSTACLE_WALL_TOP && (fObstacles & OBSTACLE_WALL_TOP) == OBSTACLE_WALL_TOP) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_TOP;
                                }
                                if (wallNext == OBSTACLE_WALL_BOTTOM && (fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_BOTTOM;
                                }
                                if (wallCurrent == OBSTACLE_WALL_TOP && (fObstacles & OBSTACLE_WALL_TOP) == 0) {
                                    goTo(WAY_UP);
                                    wallNext = OBSTACLE_WALL_RIGHT;
                                }
                                else if (wallCurrent == OBSTACLE_WALL_BOTTOM && (fObstacles & OBSTACLE_WALL_BOTTOM) == 0) {
                                    goTo(WAY_DOWN);
                                    wallNext = OBSTACLE_WALL_RIGHT;
                                }
                                else if ((fObstacles & OBSTACLE_WALL_LEFT) == OBSTACLE_WALL_LEFT) {
                                    lockWay(WAY_LEFT);
                                    wallCurrent = OBSTACLE_WALL_LEFT;
                                    if ((posY > getY(path[nextCheckPointIndex]) || (fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) && (fObstacles & OBSTACLE_WALL_TOP) == 0) {
                                        goTo(WAY_UP);
                                    }
                                    else if ((fObstacles & OBSTACLE_WALL_BOTTOM) == 0) {
                                        goTo(WAY_DOWN);
                                    }
                                    else {
                                        goTo(WAY_NONE);
                                    }
                                }
                            }
                            else if ((wayCurrent & WAY_RIGHT) == WAY_RIGHT) {
                                if (wallNext == OBSTACLE_WALL_TOP && (fObstacles & OBSTACLE_WALL_TOP) == OBSTACLE_WALL_TOP) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_TOP;
                                }
                                if (wallNext == OBSTACLE_WALL_BOTTOM && (fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_BOTTOM;
                                }
                                if (wallCurrent == OBSTACLE_WALL_TOP && (fObstacles & OBSTACLE_WALL_TOP) == 0) {
                                    goTo(WAY_UP);
                                    wallNext = OBSTACLE_WALL_LEFT;
                                }
                                else if (wallCurrent == OBSTACLE_WALL_BOTTOM && (fObstacles & OBSTACLE_WALL_BOTTOM) == 0) {
                                    goTo(WAY_DOWN);
                                    wallNext = OBSTACLE_WALL_LEFT;
                                }
                                else if ((fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) {
                                    lockWay(WAY_RIGHT);
                                    wallCurrent = OBSTACLE_WALL_RIGHT;
                                    if ((posY > getY(path[nextCheckPointIndex]) || (fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) && (fObstacles & OBSTACLE_WALL_TOP) == 0) {
                                        goTo(WAY_UP);
                                    }
                                    else if ((fObstacles & OBSTACLE_WALL_BOTTOM) == 0) {
                                        goTo(WAY_DOWN);
                                    }
                                    else {
                                        goTo(WAY_NONE);
                                    }
                                }
                            }
                            else if ((wayCurrent & WAY_UP) == WAY_UP) {
                                if (wallNext == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == OBSTACLE_WALL_LEFT) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_LEFT;
                                }
                                if (wallNext == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_RIGHT;
                                }
                                if (wallCurrent == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                    goTo(WAY_LEFT);
                                    wallNext = OBSTACLE_WALL_BOTTOM;
                                }
                                else if (wallCurrent == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                    goTo(WAY_RIGHT);
                                    wallNext = OBSTACLE_WALL_BOTTOM;
                                }
                                else if ((fObstacles & OBSTACLE_WALL_TOP) == OBSTACLE_WALL_TOP) {
                                    lockWay(WAY_UP);
                                    wallCurrent = OBSTACLE_WALL_TOP;
                                    if ((posX > getX(path[nextCheckPointIndex]) || (fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                        goTo(WAY_LEFT);
                                    }
                                    else if ((fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                        goTo(WAY_RIGHT);
                                    }
                                    else {
                                        goTo(WAY_NONE);
                                    }
                                }
                            }
                            else if ((wayCurrent & WAY_DOWN) == WAY_DOWN) {
                                if (wallNext == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == OBSTACLE_WALL_LEFT) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_LEFT;
                                }
                                if (wallNext == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) {
                                    wallNext = OBSTACLE_NONE;
                                    wallCurrent = OBSTACLE_WALL_RIGHT;
                                }
                                if (wallCurrent == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                    goTo(WAY_LEFT);
                                    wallNext = OBSTACLE_WALL_TOP;
                                }
                                else if (wallCurrent == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                    goTo(WAY_RIGHT);
                                    wallNext = OBSTACLE_WALL_TOP;
                                }
                                else if ((fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) {
                                    lockWay(WAY_DOWN);
                                    wallCurrent = OBSTACLE_WALL_BOTTOM;
                                    if ((posX > getX(path[nextCheckPointIndex]) || (fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                        goTo(WAY_LEFT);
                                    }
                                    else if ((fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                        goTo(WAY_RIGHT);
                                    }
                                    else {
                                        goTo(WAY_NONE);
                                    }
                                }
                            }
                        }                  
                    }
                break;
                /*** END DROPING STATE  ***/
            
                /*** BEGIN ATTACKING STATE  ***/
                case STATE_ATTACKING:
                    if (isMarvWithInRange(DISTANCE_EVADING) && (Hero.m_blDynamite || Hero.m_nPickAxe > 0)) {
                        changeState(STATE_EVADING);
                    }
                    else if (isMarvWithInRange(DISTANCE_ATTACKING)) {
                        tX = (short)Hero.m_nPosX;
                        tY = (short)(Hero.m_nPosY - (Hero.animationCurrent.GetCurFrmHeight() >> 1));
                        if (!correctionActivated && (goalPosX != tX || goalPosY != tY)) {
                            goalPosX = tX;
                            goalPosY = tY;
                            goTo(tX, tY, false);
                        }
                    }
                    else {
                        changeState(STATE_IDLE);
                    }
                break;
                /*** END ATTACKING STATE  ***/
            
                /*** BEGIN EVADING STATE  ***/
                case STATE_EVADING:
                    if (isMarvWithInRange(DISTANCE_EVADING) && (Hero.m_blDynamite || Hero.m_nPickAxe > 0)) {
                        if (Hero.m_nSpeedLeft > 0 || Hero.m_nSpeedRight > 0) {
                            wayBlocked &= ~(WAY_LEFT | WAY_RIGHT);
                        }
                        if (Hero.m_nSpeedUp > 0 || Hero.m_nSpeedDown > GameWorld.c_nGravityPerTick) {
                            wayBlocked &= ~(WAY_UP | WAY_DOWN);
                        }
                  
                        fObstacles = checkVisibleArea();
                        fMarv = checkVisibleMarv();

                        if (wayCurrent == WAY_NONE) {
                            if ((posX < Hero.m_nPosX || wallCurrent == OBSTACLE_WALL_RIGHT) && isWayFree(WAY_LEFT) && wallCurrent != OBSTACLE_WALL_LEFT && (fMarv & (OBSTACLE_MARV_LEFT | OBSTACLE_MARV_LEFTTOP | OBSTACLE_MARV_LEFTBOTTOM)) == 0) { // Marv is on the right side of MortalFly
                                goTo(WAY_LEFT);
                            }
                            else if (isWayFree(WAY_RIGHT) && wallCurrent != OBSTACLE_WALL_RIGHT && (fMarv & (OBSTACLE_MARV_RIGHT | OBSTACLE_MARV_RIGHTTOP | OBSTACLE_MARV_RIGHTBOTTOM)) == 0) { // Marv is on the left side of MortalFly
                                goTo(WAY_RIGHT);
                            }
                            else {
                                goTo(WAY_NONE);
                            }
                        }
                        else if ((wayCurrent & WAY_LEFT) == WAY_LEFT) {
                            if ((fMarv & (OBSTACLE_MARV_LEFT | OBSTACLE_MARV_LEFTTOP | OBSTACLE_MARV_LEFTBOTTOM)) != 0) {
                                lockWay(WAY_LEFT);
                                goTo(WAY_RIGHT);
                            }
                            else if ((fObstacles & OBSTACLE_WALL_LEFT) == OBSTACLE_WALL_LEFT) {
                                wallCurrent = OBSTACLE_WALL_LEFT;
                                lockWay(WAY_LEFT);
                                if (posY < Hero.m_nPosY || !isWayFree(WAY_DOWN)) {
                                    goTo(WAY_UP);
                                }
                                else if (isWayFree(WAY_DOWN)) {
                                    goTo(WAY_DOWN);
                                }
                                else {
                                    goTo(WAY_NONE);
                                }
                            }
                        }                                         
                        else if ((wayCurrent & WAY_RIGHT) == WAY_RIGHT) {
                            if ((fMarv & (OBSTACLE_MARV_RIGHT | OBSTACLE_MARV_RIGHTTOP | OBSTACLE_MARV_RIGHTBOTTOM)) != 0) {
                                lockWay(WAY_RIGHT);
                                goTo(WAY_LEFT);
                            }
                            else if ((fObstacles & OBSTACLE_WALL_RIGHT) == OBSTACLE_WALL_RIGHT) {
                                wallCurrent = OBSTACLE_WALL_RIGHT;
                                lockWay(WAY_RIGHT);
                                if (posY < Hero.m_nPosY || !isWayFree(WAY_DOWN)) {
                                    goTo(WAY_UP);
                                }
                                else if (isWayFree(WAY_DOWN)) {
                                    goTo(WAY_DOWN);
                                }
                                else {
                                    goTo(WAY_NONE);
                                }
                            }
                        }
                        else if ((wayCurrent & WAY_UP) == WAY_UP) {
                            if ((fMarv & (OBSTACLE_MARV_TOP | OBSTACLE_MARV_LEFTTOP | OBSTACLE_MARV_RIGHTTOP)) != 0) {
                                lockWay(WAY_UP);
                                goTo(WAY_DOWN);
                            }
                            else if (wallCurrent == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                wallCurrent = OBSTACLE_NONE;
                                goTo(WAY_LEFT);
                            }
                            else if (wallCurrent == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                wallCurrent = OBSTACLE_NONE;
                                goTo(WAY_RIGHT);
                            }
                            else if ((fObstacles & OBSTACLE_WALL_TOP) == OBSTACLE_WALL_TOP) {
                                lockWay(WAY_UP);
                                if (isWayFree(WAY_DOWN)) {
                                    goTo(WAY_DOWN);
                                }
                                else {
                                    goTo(WAY_NONE);
                                }
                            }
                        }
                        else if ((wayCurrent & WAY_DOWN) == WAY_DOWN) {
                            if ((fMarv & (OBSTACLE_MARV_BOTTOM | OBSTACLE_MARV_LEFTBOTTOM | OBSTACLE_MARV_RIGHTBOTTOM)) != 0) {
                                lockWay(WAY_DOWN);
                                goTo(WAY_UP);
                            }
                            else if (wallCurrent == OBSTACLE_WALL_LEFT && (fObstacles & OBSTACLE_WALL_LEFT) == 0) {
                                wallCurrent = OBSTACLE_NONE;
                                goTo(WAY_LEFT);
                            }
                            else if (wallCurrent == OBSTACLE_WALL_RIGHT && (fObstacles & OBSTACLE_WALL_RIGHT) == 0) {
                                wallCurrent = OBSTACLE_NONE;
                                goTo(WAY_RIGHT);
                            }
                            else if ((fObstacles & OBSTACLE_WALL_BOTTOM) == OBSTACLE_WALL_BOTTOM) {
                                lockWay(WAY_DOWN);
                                if (isWayFree(WAY_UP)) {
                                    goTo(WAY_UP);
                                }
                                else {
                                    goTo(WAY_NONE);
                                }
                            }
                        }
                    }
                    else if (isMarvWithInRange(DISTANCE_ATTACKING)) {
                        changeState(STATE_ATTACKING);
                    }
                    else {
                        changeState(STATE_IDLE);
                    }
                break;
                /*** END EVADING STATE  ***/

                /*** BEGIN IDLE STATE  ***/
                case STATE_IDLE:
                    if (isMarvWithInRange(DISTANCE_EVADING) && (Hero.m_blDynamite || Hero.m_nPickAxe > 0)) {
                        changeState(STATE_EVADING);
                    }
                    else if (isMarvWithInRange(DISTANCE_ATTACKING)) {
                        changeState(STATE_ATTACKING);
                    }
                    else {
                        counter++;
                        if (counter >= STATE_IDLE_WAIT_TICKS) {
                            changeState(STATE_PATROLING);
                        }
                    }
                break;
                /*** END IDLE STATE  ***/

                /*** BEGIN DEAD STATE  ***/
                case STATE_DEAD:
                break;
                /*** END DEAD STATE  ***/
            }
            move();
            checkIntersect();
        }
    }
   
    /**
     * Method implements calculation of movements. Here is processing current monsetr mode, processing moving by check point ad other stuff
     */
    private void move() {
        if (isActive) {
            if (correctionActivated) {
                lStepX = lOldStepX + FP.mul(lOffSetX, correctionArray[correctionIndex]);
                lStepY = lOldStepY + FP.mul(lOffSetY, correctionArray[correctionIndex]);
                correctionIndex ++;
                if (correctionIndex == (CORRECTION_COUNT + 1)) {
                    correctionActivated = false;
                    if (pathMustBeenCorrected) {
                        pathMustBeenCorrected = false;
                        goTo(goalPosX, goalPosY, false);
                    }
                }
            }
            lPosX += lStepX;
            lPosY += lStepY;
            posX = FP.toShort(lPosX);
            posY = FP.toShort(lPosY);

            posLeft   = (short)(posX - (monsterWidth >> 1));
            posTop    = (short)(posY - monsterHeight);
            posRight  = (short)(posX + (monsterWidth >> 1));
            posBottom = (short)posY;

            currentAnimation.Tick();
         
            if ((currentAnimation.nStateFlg & Animation.cnANIM_STATE_END_OF_CYCLE) != 0) {
                if (currentState == STATE_DEAD) {
                    isActive = false;
                }
                else if (currentState == STATE_ATTACKING) {
                    if (mode == MODE_TURN_LEFT) {
                        moveLeft(Constants.ANIMATION_DRAGON_ATTACK_LEFT);
                    }
                    else if (mode == MODE_TURN_RIGHT) {
                        moveRight(Constants.ANIMATION_DRAGON_ATTACK_RIGHT);
                    }
                }
                else {
                    if (mode == MODE_TURN_LEFT) {
                        moveLeft(Constants.ANIMATION_DRAGON_MOVE_LEFT);
                    }
                    else if (mode == MODE_TURN_RIGHT) {
                        moveRight(Constants.ANIMATION_DRAGON_MOVE_RIGHT);
                    }
                }
            }
        }
    }
   
    /**
     * Get index 0f the next check point
     */
    private void getNextCheckPoint() {
        prevCheckPointIndex = nextCheckPointIndex;
        nextCheckPointIndex += nextCheckPointDir;
        if (nextCheckPointIndex < 0) {
            nextCheckPointIndex = 1;
            if (nextCheckPointIndex >= path.length) {
                nextCheckPointIndex = 0;
            }
            nextCheckPointDir = 1;
        }
        else if (nextCheckPointIndex > path.length - 1) {
            nextCheckPointIndex = (byte)(path.length - 2);
            if (nextCheckPointIndex < 0) {
                nextCheckPointIndex = (byte)(path.length - 1);
            }
            nextCheckPointDir = -1;
        }
        goalPosX = getX(path[nextCheckPointIndex]);
        goalPosY = getY(path[nextCheckPointIndex]);
    }

    /**
     * Method process on exit state
     * @param prevState the previous State
     */
    public void onExitState(byte prevState) {
        switch (prevState) {
            case STATE_PATROLING:
            break;

            case STATE_SEARCHING:
                wayCurrent = wayBlocked = WAY_NONE;
                wallCurrent = wallNext = OBSTACLE_NONE;
            break;

            case STATE_ATTACKING:
            break;

            case STATE_EVADING:
                wayCurrent = wayBlocked = WAY_NONE;
            break;

            case STATE_DEAD:
            break;
        }
    }
   
    /**
     * Method process on enter state
     * @param newState the new State
     */
    public void onEnterState(byte newState) {
        short tX, tY, dX, dY,
              aLeft, aTop, aRight, aBottom,
              marvLeft, marvTop, marvRight, marvBottom;
        int dMarv, dPoint;
        byte i;

        switch (newState) {
            case STATE_PATROLING:
                if (mode == MODE_MOVE_LEFT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_LEFT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_LEFT];
                    currentAnimation.Reset();
                }
                else if (mode == MODE_MOVE_RIGHT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_RIGHT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_RIGHT];
                    currentAnimation.Reset();
                }
                lStep = FP.toFP(STEP_STATE_PATROLING);

                boolean isVisibleCheckPoint = false;
                boolean isReachableCheckPoint = false;
                if (Hero.m_blDynamite || Hero.m_nPickAxe > 0) {
                    // Find check point fo continue patroling
                    dX = (short)(posX - Hero.m_nPosX);
                    dY = (short)(posY - Hero.m_nPosY);
                    dMarv = dX * dX + dY * dY;
                    for (i = 0; i < path.length; i++) {
                        tX = getX(path[i]);
                        tY = getY(path[i]);
                        if (GameWorld.isPointsWithInRange(posX, posY, tX, tY)) {
                            isVisibleCheckPoint = true;

                            aLeft = (short)(tX - (monsterWidth >> 1));
                            aTop = (short)(tY - monsterHeight);
                            aRight = (short)(tX + (monsterWidth >> 1));
                            aBottom = (short)(tY);

                            marvLeft = (short)(Hero.m_nPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1));
                            marvTop = (short)(Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight());
                            marvRight = (short)(Hero.m_nPosX + (short)(Hero.animationCurrent.GetCurFrmWidth() >> 1));
                            marvBottom = (short)(Hero.m_nPosY);

                            if (isIntersect(marvLeft, marvTop, marvRight, marvBottom, aLeft - DISTANCE_EVADING, aTop - DISTANCE_EVADING, aRight + DISTANCE_EVADING, aBottom + DISTANCE_EVADING) && GameWorld.isPointsWithInRange(tX, tY, Hero.m_nPosX, Hero.m_nPosY)) {
                                continue;
                            }
                            else {
                                boolean flag = false;
                                if (Hero.m_nPosX <= posX && Hero.m_nPosY < posY) {
                                    // Top-Left corner
                                    flag = isSegmentsIntersect(posRight, posTop, aRight, aTop, marvLeft, marvBottom, marvRight, marvBottom) ||
                                           isSegmentsIntersect(posRight, posTop, aRight, aTop, marvRight, marvTop, marvRight, marvBottom) ||
                                           isSegmentsIntersect(posLeft, posBottom, aLeft, aBottom, marvLeft, marvBottom, marvRight, marvBottom) ||
                                           isSegmentsIntersect(posLeft, posBottom, aLeft, aBottom, marvRight, marvTop, marvRight, marvBottom);
                                }
                                else if (Hero.m_nPosX >= posX && Hero.m_nPosY < posY) {
                                    // Top-Right corner
                                    flag = isSegmentsIntersect(posLeft, posTop, aLeft, aTop, marvLeft, marvTop, marvLeft, marvBottom) ||
                                           isSegmentsIntersect(posLeft, posTop, aLeft, aTop, marvLeft, marvBottom, marvRight, marvBottom) ||
                                           isSegmentsIntersect(posRight, posBottom, aRight, aBottom, marvLeft, marvTop, marvLeft, marvBottom) ||
                                           isSegmentsIntersect(posRight, posBottom, aRight, aBottom, marvLeft, marvBottom, marvRight, marvBottom);
                                }
                                else if (Hero.m_nPosX < posX && Hero.m_nPosY >= posY) {
                                    // Bottom-Left corner 
                                    flag = isSegmentsIntersect(posLeft, posTop, aLeft, aTop, marvLeft, marvTop, marvRight, marvTop) ||
                                           isSegmentsIntersect(posLeft, posTop, aLeft, aTop, marvRight, marvTop, marvRight, marvBottom) ||
                                           isSegmentsIntersect(posRight, posBottom, aRight, aBottom, marvLeft, marvTop, marvRight, marvTop) ||
                                           isSegmentsIntersect(posRight, posBottom, aRight, aBottom, marvRight, marvTop, marvRight, marvBottom);
                                }
                                else if (Hero.m_nPosX > posX && Hero.m_nPosY >= posY) {
                                    // Bottom-Right corner
                                    flag = isSegmentsIntersect(posRight, posTop, aRight, aTop, marvLeft, marvTop, marvRight, marvTop) ||
                                           isSegmentsIntersect(posRight, posTop, aRight, aTop, marvLeft, marvTop, marvLeft, marvBottom) ||
                                           isSegmentsIntersect(posLeft, posBottom, aLeft, aBottom, marvLeft, marvTop, marvRight, marvTop) ||
                                           isSegmentsIntersect(posLeft, posBottom, aLeft, aBottom, marvLeft, marvTop, marvLeft, marvBottom);
                                }
                                if (!flag) {
                                    isReachableCheckPoint = true;

                                    prevCheckPointIndex = nextCheckPointIndex;
                                    nextCheckPointIndex = i;
                                    goalPosX = getX(path[nextCheckPointIndex]);
                                    goalPosY = getY(path[nextCheckPointIndex]);
                                    goTo(goalPosX, goalPosY, true);
                                    break;
                                }
                            }
                        }
                    }
                    if (!isReachableCheckPoint) {
                        changeState(STATE_IDLE);
                    }
                }
                else {
                    if (GameWorld.isPointsWithInRange(posX, posY, getX(path[nextCheckPointIndex]), getY(path[nextCheckPointIndex]))) { // Checks is next check point is visible
                        isVisibleCheckPoint = true;
                        goalPosX = getX(path[nextCheckPointIndex]);
                        goalPosY = getY(path[nextCheckPointIndex]);
                        goTo(goalPosX, goalPosY, true);
                    }
                    else {
                        // Find check point fo continue patroling
                        for (i = 0; i < path.length; i++) {
                            if (GameWorld.isPointsWithInRange(posX, posY, getX(path[i]), getY(path[i]))) {
                                isVisibleCheckPoint = true;
                           
                                prevCheckPointIndex = nextCheckPointIndex;
                                nextCheckPointIndex = i;
                                goalPosX = getX(path[nextCheckPointIndex]);
                                goalPosY = getY(path[nextCheckPointIndex]);
                                goTo(goalPosX, goalPosY, true);
                                break;
                            }
                        }
                    }
                }
                if (!isVisibleCheckPoint) {
                    // No one visible check points -> transist to SEARCHING state
                    changeState(STATE_SEARCHING);
                }
            break;

            case STATE_SEARCHING:
                wayCurrent = wayBlocked = WAY_NONE;
                wallCurrent = wallNext = OBSTACLE_NONE;
                if (mode == MODE_MOVE_LEFT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_LEFT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_LEFT];
                    currentAnimation.Reset();
                }
                else if (mode == MODE_MOVE_RIGHT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_RIGHT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_RIGHT];
                    currentAnimation.Reset();
                }
                lStep = FP.toFP(STEP_STATE_PATROLING);
            break;

            case STATE_EVADING:
                wayCurrent = wayBlocked = WAY_NONE;
                wallCurrent = OBSTACLE_NONE;
                if (mode == MODE_MOVE_LEFT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_ATTACK_LEFT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_ATTACK_LEFT];
                    currentAnimation.Reset();
                }
                else if (mode == MODE_MOVE_RIGHT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_ATTACK_RIGHT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_ATTACK_RIGHT];
                    currentAnimation.Reset();
                }
                lStep = FP.toFP(STEP_STATE_ATTACKING);
            break;

            case STATE_ATTACKING:
                wayCurrent = wayBlocked = WAY_NONE;
                goTo((short)Hero.m_nPosX, (short)(Hero.m_nPosY - (Hero.animationCurrent.GetCurFrmHeight() >> 1)), true);
                if (mode == MODE_MOVE_LEFT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_ATTACK_LEFT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_ATTACK_LEFT];
                    currentAnimation.Reset();
                }
                else if (mode == MODE_MOVE_RIGHT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_ATTACK_RIGHT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_ATTACK_RIGHT];
                    currentAnimation.Reset();
                }
                lStep = FP.toFP(STEP_STATE_ATTACKING);
            break;

            case STATE_IDLE:
                counter = 0;
                goTo(WAY_NONE);
                if (mode == MODE_MOVE_LEFT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_LEFT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_LEFT];
                    currentAnimation.Reset();
                }
                else if (mode == MODE_MOVE_RIGHT && currentAnimation.nAnimID != Constants.ANIMATION_DRAGON[Constants.ANIMATION_DRAGON_MOVE_RIGHT]) {
                    currentAnimation = animations[Constants.ANIMATION_DRAGON_MOVE_RIGHT];
                    currentAnimation.Reset();
                }
            break;

            case STATE_DEAD:
                goTo(WAY_NONE);
                currentAnimation = animations[Constants.ANIMATION_BEAST_DEATH];
                mode = MODE_DEATH;
            break;
        }
    }

    public static final long FP_SQRT = FP.FP >> 1;     // float point precision
    static public long sqrt(long L) {
        long temp, div;
        long rslt = L;

        if (L <= 0) return 0;
        else if ((L & 0x00000000FFFF0000) > 0) {
            if ((L & 0x00000000FF000000) > 0)
                div = 0x3FFF;
            else
                div = 0x3FF;
        }
        else {
            if ((L & 0x000000000000FF00) > 0)
                div = 0x3F;
            else
                div = (L > 4) ? 0x0000000000000007 : L;
        }

        while (true) {
            temp = L / div + div; 
            div = temp >> 1; 
            div += temp & 1;
            if (rslt > div)
                rslt = div;
            else {
                if (1 / rslt == rslt - 1 && 1 % rslt == 0) rslt--;
                return rslt << FP_SQRT;
            }
        }
    }

    /**
     * Method calculates direction of moving to point (x,y). Calculation is going with current speed of moving
     */
    private void goTo(short x, short y, boolean startSliding) {
        long l, dx, dy, dl;
   
        dx = FP.toFP(x) - lPosX;
        dy = FP.toFP(y) - lPosY;
        l = sqrt(FP.mul(dx, dx) + FP.mul(dy, dy));
        if (l > 0) {
            lOldStepX = lStepX; 
            lOldStepY = lStepY;

            dl =  FP.div(dx, l);
            lStepX = FP.mul(lStep, dl);
            dl =  FP.div(dy, l);
            lStepY = FP.mul(lStep, dl);

            lOffSetX = lStepX - lOldStepX;
            lOffSetY = lStepY - lOldStepY;

            if (startSliding) {
                correctionActivated = true;
                pathMustBeenCorrected = true;
                correctionIndex = 0;
            }
        }

        if (x < posX) {
            turnLeft();
        }
        else if (x > posX) {
            turnRight();
        }
    }
   
    /**
     *
     */
    private void goTo(byte direction) {
        lOldStepX = lStepX; 
        lOldStepY = lStepY;

        if ((direction & WAY_UP) == WAY_UP) {
            lStepX = 0;
            lStepY = -lStep;
            setWay(WAY_UP);
        }
        else if ((direction & WAY_DOWN) == WAY_DOWN) {
            lStepX = 0;
            lStepY = lStep;
            setWay(WAY_DOWN);
        }
        else if ((direction & WAY_LEFT) == WAY_LEFT) {
            lStepX = -lStep;
            lStepY = 0;
            turnLeft();
            setWay(WAY_LEFT);
        }
        else if ((direction & WAY_RIGHT) == WAY_RIGHT) {
            lStepX = lStep;
            lStepY = 0;
            turnRight();
            setWay(WAY_RIGHT);
        }
        else if ((direction & WAY_NONE) == WAY_NONE){
            lStepX = 0;
            lStepY = 0;
            setWay(WAY_NONE);
        }
        if ((wayCurrent & direction) != direction) {
            lOffSetX = lStepX - lOldStepX;
            lOffSetY = lStepY - lOldStepY;

            correctionActivated = true;
            pathMustBeenCorrected = false;
            correctionIndex = 0;
        }
    }

    private void moveLeft(byte animationId) {
        currentAnimation = animations[animationId];
        currentAnimation.Reset();
        mode = MODE_MOVE_LEFT;
    }
   
    private void moveRight(byte animationId) {
        currentAnimation = animations[animationId];
        currentAnimation.Reset();
        mode = MODE_MOVE_RIGHT;
    }
   
    /**
     * Turns monster on the left side
     */
    private void turnLeft() {
        if (mode != MODE_MOVE_LEFT && mode != MODE_TURN_LEFT) {
            //#if IMGGROUP_12x12xLimited
                switch (currentState) {
                    case STATE_PATROLING:
                    case STATE_SEARCHING:
                        moveLeft(Constants.ANIMATION_DRAGON_MOVE_LEFT);
                    break;
               
                    case STATE_EVADING:
                    case STATE_ATTACKING:
                        moveLeft(Constants.ANIMATION_DRAGON_ATTACK_LEFT);
                    break;
                }
            //#else
                currentAnimation = animations[Constants.ANIMATION_DRAGON_TURN];
                currentAnimation.nStateFlg |= Animation.cnANIM_STATE_PLAY_REVERSED;
                currentAnimation.Reset();
                mode = MODE_TURN_LEFT;
            //#endif
        }
    }
   
    /**
     * Turns monster on the right side
     */
    private void turnRight() {
        if (mode != MODE_MOVE_RIGHT && mode != MODE_TURN_RIGHT) {
            //#if IMGGROUP_12x12xLimited
                switch (currentState) {
                    case STATE_PATROLING:
                    case STATE_SEARCHING:
                        moveRight(Constants.ANIMATION_DRAGON_MOVE_RIGHT);
                    break;
               
                    case STATE_EVADING:
                    case STATE_ATTACKING:
                        moveRight(Constants.ANIMATION_DRAGON_ATTACK_RIGHT);
                    break;
                }
            //#else
                currentAnimation = animations[Constants.ANIMATION_DRAGON_TURN];
                currentAnimation.nStateFlg &= ~Animation.cnANIM_STATE_PLAY_REVERSED;
                currentAnimation.Reset();
                mode = MODE_TURN_RIGHT;
            //#endif
        }
    }
   
    /**
     * Get the x coordinate from check pint
     * @param checkPoint is the pair of x and y coordinates in the format <code>((x << 16) & 0xFFFF0000) | (y & 0x0000FFFF)</code>
     * @return the x coordinate
     */
    private short getX(int checkPoint) {
        return (short)((checkPoint >> 16) & 0x0000ffff);
    }
   
    /**
     * Get the y coordinate from check pint
     * @param checkPoint is the pair of x and y coordinates in the format <code>((x << 16) & 0xFFFF0000) | (y & 0x0000FFFF)</code>
     * @return the y coordinate
     */
    private short getY(int checkPoint) {
        return (short)(checkPoint & 0x0000ffff);
    }
  
    /** 
     * Checking intersections of two segment
     * @param ax1
     * @param ay1
     * @param ax2
     * @param ay2
     * @param bx1
     * @param by1
     * @param bx2
     * @param by2
     * @return true if segmens intersect and false otherwise
     */
    private boolean isSegmentsIntersect(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) {
        long v1 = (bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1);
        long v2 = (bx2 - bx1) * (ay2 - by1) - (by2 - by1) * (ax2 - bx1);
        long v3 = (ax2 - ax1) * (by1 - ay1) - (ay2 - ay1) * (bx1 - ax1);
        long v4 = (ax2 - ax1) * (by2 - ay1) - (ay2 - ay1) * (bx2 - ax1);
        return ((v1 * v2) < 0) && ((v3 * v4) < 0);
    }

    /**
     *
     */
    private boolean isMarvWithInRange(short distance) {
        int tPosX, tPosY, marvPosX, marvPosY, monsterPosX, monsterPosY;
        int marvLeft = Hero.m_nPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1);
        int marvTop = Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight();
        int marvRight = Hero.m_nPosX + (Hero.animationCurrent.GetCurFrmWidth() >> 1);
        int marvBottom = Hero.m_nPosY;
        if (Hero.m_blDynamite) {
            if (Hero.m_blDirRight) {
                marvRight += Hero.animItemDynamite.GetCurFrmWidth() >> 1;
            }
            else {
                marvLeft -= Hero.animItemDynamite.GetCurFrmWidth() >> 1;
            }
        }
        if (Hero.m_nPickAxe > 0) {
            if (Hero.m_blDirRight) {
                marvRight += Hero.animItemPickAxeRight.GetCurFrmWidth() + Hero.animItemPickAxeRight.getLayerProperty(0, Constants.LAYERPROPERTY_OFFSET_X);
            }
            else {
                marvLeft += Hero.animItemPickAxeleft.getLayerProperty(0, Constants.LAYERPROPERTY_OFFSET_X) - 1;
            }
        }
        tPosX = (marvLeft + marvRight) >> 1;
        tPosY = (marvTop + marvBottom) >> 1;
        if (tPosX < posLeft) {
            // Marv is on the left side
            monsterPosX = posLeft;
        }
        else if (tPosX > posRight) {
            // Marv is on the right
            monsterPosX = posRight;
        }
        else {
            monsterPosX = (posLeft + posRight) >> 1;
        }
        if (tPosY < posTop) {
            // Marv is above the monster
            monsterPosY = posTop;
        }
        else if (tPosY > posBottom) {
            // Marv is under the monster
            monsterPosY = posBottom;
        }
        else {
            monsterPosY = (posTop + posBottom) >> 1;
        }
        tPosX = (posLeft + posRight) >> 1;
        tPosY = (posTop + posBottom) >> 1;
        if (tPosX < marvLeft) {
            // Marv is on the left side
            marvPosX = marvLeft;
        }
        else if (tPosX > marvRight) {
            // Marv is on the right
            marvPosX = marvRight;
        }
        else {
            marvPosX = (marvLeft + marvRight) >> 1;
        }
        if (tPosY < marvTop) {
            // Marv is above the monster
            marvPosY = marvTop;
        }
        else if (tPosY > marvBottom) {
            // Marv is under the monster
            marvPosY = marvBottom;
        }
        else {
            marvPosY = (marvTop + marvBottom) >> 1;
        }
        return Hero.m_nDeathTicks == 0 && Hero.m_nImmortalTicks == 0 && 
               (Hero.m_nKeybMode == Hero.nModeNormal || Hero.m_nKeybMode == Hero.nModeLadder || Hero.m_nKeybMode == Hero.nModeLadderStart || Hero.m_nKeybMode == Hero.nModeLadderFinish) &&
               isIntersect(marvLeft, marvTop, marvRight, marvBottom, posLeft - distance, posTop - distance, posRight + distance, posBottom + distance) &&
               GameWorld.isPointsWithInRange(marvPosX, marvPosY, monsterPosX, monsterPosY);
    }

    /**
     *
     */
    private int checkVisibleMarv() {
        int result = OBSTACLE_MARV_NONE;

        int aLeft, aTop, aRight, aBottom;
      
        int marvLeft = Hero.m_nPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1);
        int marvTop = Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight();
        int marvRight = Hero.m_nPosX + (Hero.animationCurrent.GetCurFrmWidth() >> 1);
        int marvBottom = Hero.m_nPosY;
        if (Hero.m_blDynamite) {
            if (Hero.m_blDirRight) {
                marvRight += Hero.animItemDynamite.GetCurFrmWidth() >> 1;
            }
            else {
                marvLeft -= Hero.animItemDynamite.GetCurFrmWidth() >> 1;
            }
        }
        if (Hero.m_nPickAxe > 0) {
            if (Hero.m_blDirRight) {
                marvRight += Hero.animItemPickAxeRight.GetCurFrmWidth() + Hero.animItemPickAxeRight.getLayerProperty(0, Constants.LAYERPROPERTY_OFFSET_X);
            }
            else {
                marvLeft += Hero.animItemPickAxeleft.getLayerProperty(0, Constants.LAYERPROPERTY_OFFSET_X) - 1;
            }
        }

        aLeft = posLeft - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET;
        aTop = posTop - DISTANCE_DETECTING_OFFSET;
        aRight = posRight + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET;
        aBottom = posBottom + DISTANCE_DETECTING_OFFSET;
      
        if (isIntersect(aLeft, aTop, (aLeft + aRight) >> 1, aBottom, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_LEFT;
        }
        else if (isIntersect((aLeft + aRight) >> 1, aTop, aRight, aBottom, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_RIGHT;
        }

        aLeft = posLeft - DISTANCE_DETECTING_OFFSET;
        aTop = posTop - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET;
        aRight = posRight + DISTANCE_DETECTING_OFFSET;
        aBottom = posBottom + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET;
      
        if (isIntersect(aLeft, aTop, aRight, (aTop + aBottom) >> 1, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_TOP;
        }
        else if (isIntersect(aLeft, (aTop + aBottom) >> 1, aRight, aBottom, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_BOTTOM;
        }

        if (isIntersect(posLeft - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET, posTop - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET, posLeft - DISTANCE_DETECTING_OFFSET, posTop - DISTANCE_DETECTING_OFFSET, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_LEFTTOP;
        }
        else if (isIntersect(posRight + DISTANCE_DETECTING_OFFSET, posTop - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET, posRight + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET, posTop - DISTANCE_DETECTING_OFFSET, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_RIGHTTOP;
        }
        else if (isIntersect(posLeft - DISTANCE_DETECTING - DISTANCE_DETECTING_OFFSET, posBottom + DISTANCE_DETECTING_OFFSET, posLeft - DISTANCE_DETECTING_OFFSET, posBottom + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_LEFTBOTTOM;
        }
        else if (isIntersect(posRight + DISTANCE_DETECTING_OFFSET, posBottom + DISTANCE_DETECTING_OFFSET, posRight + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET, posBottom + DISTANCE_DETECTING + DISTANCE_DETECTING_OFFSET, marvLeft, marvTop, marvRight, marvBottom)) {
            result |= OBSTACLE_MARV_RIGHTBOTTOM;
        }
        return result;
    }

    /**
     * Checks obstacles
     */
    private byte checkVisibleArea() {
        int i, tileIndex;
        int aI, aJ, bI, bJ;
        int aX, aY, bX, bY;
        byte result = OBSTACLE_NONE;
      
        // Left - Right
        aX = posLeft - (TileDrawer.nTileWidth >> 1);
        aY = posTop;
        bX = posRight + (TileDrawer.nTileWidth >> 1);
        bY = posBottom;
     
        aI = aX / TileDrawer.nTileWidth;
        aJ = aY / TileDrawer.nTileWidth;
        bI = bX / TileDrawer.nTileWidth;
        bJ = bY / TileDrawer.nTileWidth;

        if (aI > -1 && aJ > -1 && bI < GameWorld.bTilesMatrix.length && bJ < GameWorld.bTilesMatrix[0].length) {
            // Left side
            for (i = aJ; i <= bJ; i++) {
                tileIndex = GameWorld.bTilesMatrix[aI][i];
                if ((TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_Collisable) != 0 && (TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_HaveMask) == 0) {
                    result |= OBSTACLE_WALL_LEFT;
                    break;
                }
            }
            // Right side
            for (i = aJ; i <= bJ; i++) {
                tileIndex = GameWorld.bTilesMatrix[bI][i];
                if ((TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_Collisable) != 0 && (TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_HaveMask) == 0) {
                    result |= OBSTACLE_WALL_RIGHT;
                    break;
                }
            }
        }

        // Up - Down
        aX = posLeft;
        aY = posTop - (TileDrawer.nTileWidth >> 1);
        bX = posRight;
        bY = posBottom + (TileDrawer.nTileWidth >> 1);
      
        aI = aX / TileDrawer.nTileWidth;
        aJ = aY / TileDrawer.nTileWidth;
        bI = bX / TileDrawer.nTileWidth;
        bJ = bY / TileDrawer.nTileWidth;

        if (aI > -1 && aJ > -1 && bI < GameWorld.bTilesMatrix.length && bJ < GameWorld.bTilesMatrix[0].length) {
            // top
            for (i = aI; i <= bI; i++) {
                tileIndex = GameWorld.bTilesMatrix[i][aJ];
                if ((TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_Collisable) != 0 && (TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_HaveMask) == 0) {
                    result |= OBSTACLE_WALL_TOP;
                    break;
                }
            }
            // Bottom
            for (i = aI; i <= bI; i++) {
                tileIndex = GameWorld.bTilesMatrix[i][bJ];
                if ((TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_Collisable) != 0 && (TileDrawer.mTiles[tileIndex].bOptions & TileDrawer.bOpt_HaveMask) == 0) {
                    result |= OBSTACLE_WALL_BOTTOM;
                    break;
                }
            }
        }      
        return result;
    }
    
    /**
     *
     */
    private void setWay(byte way) {
        wayCurrent = way;
        if ((wayCurrent & WAY_LEFT) == WAY_LEFT || (wayCurrent & WAY_RIGHT) == WAY_RIGHT) {
            wayBlocked &= ~(WAY_UP | WAY_DOWN);
        }
        else if ((wayCurrent & WAY_UP) == WAY_UP || (wayCurrent & WAY_DOWN) == WAY_DOWN) {
            wayBlocked &= ~(WAY_LEFT | WAY_RIGHT);
        }
    }
    
   /**
    *
    */
    private void lockWay(byte way) {
        wayBlocked |= way;
    }
   
    /**
     *
     */
//   private void unlockWay(byte way)
//   {
//      wayBlocked &= ~way;
//   }
   
    /**
     *
     */
    private boolean isWayFree(byte way) {
        return (wayBlocked & way) == 0;
    }

    /**
     *
     */
    public void checkIntersect() {
        if (currentState != STATE_DEAD) {
            int monsterLeft   = posLeft + (monsterWidth >> 2);
            int monsterTop    = posTop;
            int monsterRight  = posRight - (monsterWidth >> 2);
            int monsterBottom = posBottom;
         
            int marvLeft   = Hero.m_nPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1);
            int marvTop    = Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight();
            int marvRight  = Hero.m_nPosX + (Hero.animationCurrent.GetCurFrmWidth() >> 1);
            int marvBottom = Hero.m_nPosY;
            
            boolean isIntersected = Hero.m_nDeathTicks == 0 && Hero.m_nImmortalTicks == 0 && marvLeft < monsterRight && marvRight > monsterLeft && marvTop < monsterBottom && marvBottom > monsterTop;
            isIntersected = isIntersected && (Hero.m_nKeybMode == Hero.nModeNormal || Hero.m_nKeybMode == Hero.nModeLadder || Hero.m_nKeybMode == Hero.nModeLadderStart || Hero.m_nKeybMode == Hero.nModeLadderFinish);
         
            if (isIntersected) {
                if (Hero.m_blDynamite || (Hero.m_nPickAxe > 0)) {
                    changeState(STATE_DEAD);
                    Hero.monsterKilled(Constants.TYPE_MORTALFLY);
                }
                else {
                    Hero.ForceDeath(Hero.c_nAnimID_Death);
                    changeState(STATE_PATROLING);
                }
            }
        }
    }
}
